﻿using System;
using System.IO;
using System.Net;
using System.Text;
using System.Text.RegularExpressions;
using System.Web;

namespace VSSDKAssist.Assets.Helpers.KeyRegistration
{
	public abstract class Registrator
	{
		protected const string VSIPSite = "http://www.vsipmembers.com";
		protected const string VSIPUri = "http://www.vsipmembers.com/Anonymous/LoadKeyRegister.aspx";
		protected const string UserAgent = "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0; Win64; x64; .NET CLR 2.0.50727; SLCC1; Media Center PC 5.0; .NET CLR 3.0.04506; .NET CLR 3.5.21022)";
		protected const string Accept = "image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/x-ms-application, application/vnd.ms-xpsdocument, application/xaml+xml, application/x-ms-xbap, */*";
		protected const string ContentType = "application/x-www-form-urlencoded";
		protected const string AcceptLanguage = "en-us";
		protected const string AcceptEncoding = "gzip, deflate";
		protected const string Pragma = "no-cache";

		protected CookieContainer container;

		protected Registrator(
			string companyName,
			string name,
			Guid guid,
			string version)
		{
			this.CompanyName = companyName;
			this.Name = name;
			this.Guid = guid;
			this.Version = version;
		}

		private string companyName;
		public string CompanyName
		{
			get
			{
				return this.companyName;
			}
			set
			{
				if(string.IsNullOrEmpty(value))
				{
					throw new ArgumentNullException("CompanyName");
				}

				this.companyName = value;
			}
		}

		private string name;
		public string Name
		{
			get
			{
				return this.name;
			}
			set
			{
				if(string.IsNullOrEmpty(value))
				{
					throw new ArgumentNullException("Name");
				}

				this.name = value;
			}
		}

		public Guid Guid { get; set; }

		private string version;
		public string Version
		{
			get
			{
				return this.version;
			}
			set
			{
				if(string.IsNullOrEmpty(value))
				{
					throw new ArgumentNullException("Version");
				}

				Regex reg = new Regex("^[0-9]+.[0-9]+$", RegexOptions.Compiled | RegexOptions.IgnoreCase);

				if(!reg.IsMatch(value))
				{
					throw new ArgumentException("Invalid format. Should be xx.xx", "Version");
				}

				this.version = value;
			}
		}

		public virtual string Register()
		{
			try
			{
				string aspxPage = GetForm();

				if(!string.IsNullOrEmpty(aspxPage))
				{
					aspxPage = PostForm(GetPostData(aspxPage));

					if(!string.IsNullOrEmpty(aspxPage))
					{
						return GetKey(aspxPage);
					}
				}
			}
			catch(WebException ex)
			{
				throw new ApplicationException(
					String.Format("Connection error: Unable to access {0}", VSIPSite), ex);
			}

			return string.Empty;
		}

		protected static string GetInputName(string aspxPage, string inputSuffix)
		{
			string response = string.Empty;

			Regex reg = new Regex(
				string.Format("<\\w+\\s+.*name=\"(?<InputName>.*\\${0})\"", inputSuffix),
				RegexOptions.Compiled | RegexOptions.IgnoreCase);

			if(reg.IsMatch(aspxPage))
			{
				response = reg.Match(aspxPage).Groups["InputName"].Value;
			}

			return response;
		}

		protected static string GetInputValue(string aspxPage, string inputName)
		{
			string response = string.Empty;

			Regex reg = new Regex(
				string.Format("<\\w+\\s+.*name=\\s*[\"']{0}[\"'] .* value=[\"'](?<Value>[^\"']*)[\"']", inputName),
				RegexOptions.Compiled | RegexOptions.IgnoreCase);

			if(reg.IsMatch(aspxPage))
			{
				response = reg.Match(aspxPage).Groups["Value"].Value;
			}

			return response;
		}

		protected static string GetDoPostBackName(string aspxPage, string suffix)
		{
			string response = string.Empty;

			Regex reg = new Regex(
				string.Format("<\\w+\\s+.*id=\".*{0}\".*doPostBack.'(?<ElementName>.*\\${0})'", suffix),
				RegexOptions.Compiled | RegexOptions.IgnoreCase);

			if(reg.IsMatch(aspxPage))
			{
				response = reg.Match(aspxPage).Groups["ElementName"].Value;
			}

			return response;
		}

		protected abstract string GetKey(string aspxPage);

		protected virtual string GetForm()
		{
			var getRequest = (HttpWebRequest)WebRequest.Create(VSIPUri);

			this.container = new CookieContainer();
			getRequest.UserAgent = UserAgent;
			getRequest.CookieContainer = this.container;

			using(var getResponse = (HttpWebResponse)getRequest.GetResponse())
			{
				using(var reader = new StreamReader(getResponse.GetResponseStream()))
				{
					return reader.ReadToEnd();
				}
			}
		}

		protected virtual string PostForm(string postData)
		{
			var postRequest = (HttpWebRequest)WebRequest.Create(VSIPUri);

			postRequest.Accept = Accept;
			postRequest.Referer = VSIPUri;
			postRequest.Headers.Add("Accept-Language", AcceptLanguage);
			postRequest.ContentType = ContentType;
			postRequest.Headers.Add("Accept-Encoding", AcceptEncoding);
			postRequest.UserAgent = UserAgent;
			postRequest.Headers.Add("Pragma", Pragma);
			postRequest.Method = "POST";
			postRequest.ContentLength = postData.Length;
			postRequest.CookieContainer = this.container;

			using(StreamWriter postRequestStream = new StreamWriter(postRequest.GetRequestStream()))
			{
				postRequestStream.Write(postData);
				postRequestStream.Flush();
			}

			using(var postResponse = (HttpWebResponse)postRequest.GetResponse())
			{
				if(postResponse.StatusDescription.Equals("OK", StringComparison.OrdinalIgnoreCase))
				{
					using(var reader = new StreamReader(postResponse.GetResponseStream()))
					{
						return reader.ReadToEnd();
					}
				}
			}

			return string.Empty;
		}

		protected virtual string GetPostData(string aspxPage)
		{
			StringBuilder postData = new StringBuilder();

			postData.Append(string.Format("MSO_PageHashCode={0}&", GetInputValue(aspxPage, "MSO_PageHashCode")));
			postData.Append(string.Format("__SPSCEditMenu={0}&", "true"));
			postData.Append(string.Format("MSOTlPn_View={0}&", "0"));
			postData.Append(string.Format("MSOTlPn_ShowSettings={0}&", "False"));
			postData.Append(string.Format("MSOTlPn_Button={0}&", "none"));
			postData.Append(string.Format("MSOSPWebPartManager_DisplayModeName={0}&", "Browse"));
			postData.Append(string.Format("MSOSPWebPartManager_OldDisplayModeName={0}&", "Browse"));
			postData.Append(string.Format("MSOSPWebPartManager_StartWebPartEditingName={0}&", "false"));
			postData.Append(string.Format("__REQUESTDIGEST={0}&", HttpUtility.UrlEncode(GetInputValue(aspxPage, "__REQUESTDIGEST"))));
			postData.Append(string.Format("__VIEWSTATE={0}&", HttpUtility.UrlEncode(GetInputValue(aspxPage, "__VIEWSTATE"))));
			postData.Append(string.Format("__EVENTVALIDATION={0}&", HttpUtility.UrlEncode(GetInputValue(aspxPage, "__EVENTVALIDATION"))));

			return postData.ToString();
		}
	}
}
